using System;
using System.Collections.Generic;
using System.Numerics;
using TTEngine.Graphics;
using TTEngine.TTMath;
using Silk.NET.OpenGLES;
using TTEngine.Resource;
using voidbox;

namespace TTEngine.Graphics
{


    public class PackedTextureGroup : ResourceObj
    {
        public PackTexture PackedRGBA
        {
            get;
            private set;
        }
        public PackTexture PackedGray
        {
            get;
            private set;
        }
        private Dictionary<string, ushort> _mapName2Index;
        private Material _material;
        //private Material _materialCut;
        private int _elemCount;
        private float[] _elemBufData;
        public Texture2D ElemTex
        {
            get;
            private set;
        }
        private bool _elemDirty;
        private uint _maxElemCount;

        public PackedTextureGroup(ResourcePackage package, uint width, uint height, byte layercountRGBA, byte layercountGray)
            : base(package)
        {
            PackedRGBA = new PackTexture(package, width, height, layercountRGBA, TextureType.RGBA_32);
            PackedGray = new PackTexture(package, width, height, layercountGray, TextureType.R_U8);
            _mapName2Index = new Dictionary<string, ushort>();
            //_material = new Material(gl, Resource.NamedResourcePool.Default.GetShaderProgram("default"));
            ElemInit(512, 512);
            InitMat();
        }

        public uint Width
        {
            get { return PackedRGBA.Width; }
        }
        public uint Height
        {
            get { return PackedRGBA.Height; }
        }
        public int RGBALayerCount
        {
            get
            {
                return PackedRGBA.LayerCount;
            }
        }
        public int GrayLayerCount
        {
            get
            {
                return PackedGray.LayerCount;
            }
        }
        private void ElemInit(uint width, uint height)
        {
            _elemBufData = new float[width * height * 4];
            ElemTex = new Texture2D(this.Package, width, height, TextureType.RGBA_Float, null);
            _maxElemCount = width / 4 * height;
            _elemCount = 0;
            _elemDirty = false;
        }

        public void InitMat()
        {
            _material = new Material(this.Package, this.Package.GetNamedResource<ShaderProgram>("default"));
            _material._uniformTexs["texRGBA"].Value = PackedRGBA.Texture;
            _material._uniformTexs["texGray"].Value = PackedGray.Texture;

            //_materialCut = new Material(gl, Resource.NamedResourcePool.Default.GetShaderProgram("default_cut"));
            //_materialCut._uniformTexs["texRGBA"].Value = _texRGBA;
            //_materialCut._uniformTexs["texGray"].Value = _texGray;
        }

        public Material GetMaterial() => _material;
        //public Material GetMaterialCut() => _materialCut;

        public PackedTextureSprite ToSprite(PackedTextureElement elem)
        {
            var s = new PackedTextureSprite(this, elem);
            return s;
        }

        public int GetElementCount() => _elemCount;
        public uint GetMaxElementCount() => _maxElemCount;

        private void WriteElement(PackedTextureElement elem, ushort index)
        {
            elem.index = index;
            const int ElemFSize = 16;
            int Findex = index * ElemFSize;

            _elemBufData[Findex + 0] = elem.sizeTL.X;
            _elemBufData[Findex + 1] = elem.sizeTL.Y;
            _elemBufData[Findex + 2] = elem.sizeRB.X;
            _elemBufData[Findex + 3] = elem.sizeRB.Y;

            _elemBufData[Findex + 4] = elem.uvCenter.X;
            _elemBufData[Findex + 5] = elem.uvCenter.Y;
            _elemBufData[Findex + 6] = elem.uvHalfSize.X;
            _elemBufData[Findex + 7] = elem.uvHalfSize.Y;

            _elemBufData[Findex + 8] = elem.uvLayer;
            _elemBufData[Findex + 11] = (int)elem.eff;
            _elemDirty = true;
        }

        private PackedTextureElement GetElementByIndex(ushort index)
        {
            const int ElemFSize = 16;
            int Findex = index * ElemFSize;

            var elem = new PackedTextureElement();
            elem.index = index;
            elem.sizeTL = new Vector2I(0, 0);
            elem.sizeRB = new Vector2I(0, 0);
            elem.uvCenter = new Vector2(0, 0);
            elem.uvHalfSize = new Vector2(0, 0);

            elem.sizeTL.X = (int)_elemBufData[Findex + 0];
            elem.sizeTL.Y = (int)_elemBufData[Findex + 1];
            elem.sizeRB.X = (int)_elemBufData[Findex + 2];
            elem.sizeRB.Y = (int)_elemBufData[Findex + 3];

            elem.uvCenter.X = _elemBufData[Findex + 4];
            elem.uvCenter.Y = _elemBufData[Findex + 5];
            elem.uvHalfSize.X = _elemBufData[Findex + 6];
            elem.uvHalfSize.Y = _elemBufData[Findex + 7];

            elem.uvLayer = (byte)_elemBufData[Findex + 8];
            var eff = (SpriteEffect)_elemBufData[Findex + 11];
            elem.eff = eff;
            return elem;
        }

        public PackedTextureElement AddSpriteInfo(SpriteData sprite, SpriteEffect effect,Border border)
        {
            PackedTextureElement info;
            if (effect == SpriteEffect.RGBA)
                info = PackedRGBA.AddSprite(sprite, effect,border);
            else
                info = PackedGray.AddSprite(sprite, effect,border);
            return info;
        }

  

        public int AddElement(string name, ref PackedTextureElement elementsrc)
        {
            if (!_mapName2Index.TryGetValue(name, out var index))
            {
                if (_elemCount == 65536)
                    throw new Exception("[NamedElementPacked.AddElement]full element");

                index = (ushort)_elemCount;
                _mapName2Index[name] = index;
                _elemCount++;
            }

            WriteElement(elementsrc, index);
            elementsrc.index = index;
            return index;
        }

        public ushort AddElementNoname(PackedTextureElement elementsrc)
        {
            int index = _elemCount;
            if (_elemCount == 65536)
                throw new Exception("[NamedElementPacked.AddElement]full element");

            _elemCount++;
            WriteElement(elementsrc, (ushort)index);
            return (ushort)index;
        }

        public bool GetElementByName(string name, out PackedTextureElement info)
        {
            if (_mapName2Index.TryGetValue(name, out var index))
            {
                info = GetElementByIndex(index);
                return true;
            }
            info = default(PackedTextureElement);
            return false;
        }

        public void ApplyTextureData()
        {
            PackedRGBA.Apply();
            PackedGray.Apply();

            if (_elemDirty)
            {
                ElemTex.UploadImageData(0, 0, ElemTex.Width, ElemTex.Height, _elemBufData);
                _elemDirty = false;
            }
        }

        protected override void OnDestory()
        {
            PackedRGBA?.TryDecRefAndDestory();
            PackedRGBA = null;

            PackedGray?.TryDecRefAndDestory();
            PackedGray = null;

            ElemTex?.TryDecRefAndDestory();
            ElemTex = null;
        }
    }
}